#include <minix/config.h>
/*---------------------------------------------------------------------------*
 *		rs232.c - serial driver for 8250 and 16450 UARTs	     *
 *		Added support for Atari ST M68901 and YM-2149	--kub	     *
 *---------------------------------------------------------------------------*/

#include <minix/drivers.h>
#include <termios.h>
#include <signal.h>
#include "tty.h"

#if NR_RS_LINES > 0

/* 8250 constants. */
#define UART_FREQ         115200L	/* timer frequency */

/* Interrupt enable bits. */
#define IE_RECEIVER_READY       1
#define IE_TRANSMITTER_READY    2
#define IE_LINE_STATUS_CHANGE   4
#define IE_MODEM_STATUS_CHANGE  8

/* Interrupt status bits. */
#define IS_MODEM_STATUS_CHANGE  0
#define IS_TRANSMITTER_READY    2
#define IS_RECEIVER_READY       4
#define IS_LINE_STATUS_CHANGE   6

/* Line control bits. */
#define LC_2STOP_BITS        0x04
#define LC_PARITY            0x08
#define LC_PAREVEN           0x10
#define LC_BREAK             0x40
#define LC_ADDRESS_DIVISOR   0x80

/* Line status bits. */
#define LS_OVERRUN_ERR          2
#define LS_PARITY_ERR           4
#define LS_FRAMING_ERR          8
#define LS_BREAK_INTERRUPT   0x10
#define LS_TRANSMITTER_READY 0x20

/* Modem control bits. */
#define MC_DTR                  1
#define MC_RTS                  2
#define MC_OUT2                 8	/* required for PC & AT interrupts */

/* Modem status bits. */
#define MS_CTS               0x10
#define MS_RLSD              0x80       /* Received Line Signal Detect */
#define MS_DRLSD             0x08       /* RLSD Delta */

#define DATA_BITS_SHIFT         8      /* amount data bits shifted in mode */
#define DEF_BAUD             1200      /* default baud rate */

#define RS_IBUFSIZE          1024      /* RS232 input buffer size */
#define RS_OBUFSIZE          1024      /* RS232 output buffer size */

/* Input buffer watermarks.
 * The external device is asked to stop sending when the buffer
 * exactly reaches high water, or when TTY requests it.  Sending restarts
 * when the input buffer empties below the low watermark.
 */
#define RS_ILOWWATER   (1 * RS_IBUFSIZE / 4)
#define RS_IHIGHWATER  (3 * RS_IBUFSIZE / 4)

/* Output buffer low watermark.
 * TTY is notified when the output buffer empties below the low watermark, so
 * it may continue filling the buffer if doing a large write.
 */
#define RS_OLOWWATER   (1 * RS_OBUFSIZE / 4)

/* Macros to handle flow control.
 * Interrupts must be off when they are used.
 * Time is critical - already the function call for outb() is annoying.
 * If outb() can be done in-line, tests to avoid it can be dropped.
 * istart() tells external device we are ready by raising RTS.
 * istop() tells external device we are not ready by dropping RTS.
 * DTR is kept high all the time (it probably should be raised by open and
 * dropped by close of the device).
 * OUT2 is also kept high all the time.
 */
#define istart(rs) \
	(sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
		(rs)->idevready = TRUE)
#define istop(rs) \
	(sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \
		(rs)->idevready = FALSE)

/* Macro to tell if device is ready.  The rs->cts field is set to MS_CTS if
 * CLOCAL is in effect for a line without a CTS wire.
 */
#define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)

/* Macro to tell if transmitter is ready. */
#define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)

/* Macro to tell if carrier has dropped.
 * The RS232 Carrier Detect (CD) line is usually connected to the 8250
 * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem
 * Status Register.  The MS_DRLSD bit tells if MS_RLSD has just changed state.
 * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just
 * dropped.
 */
#define devhup(rs)	\
	((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)

/* Types. */
typedef unsigned char bool_t; /* boolean */

/* RS232 device structure, one per device. */
typedef struct rs232 {
	tty_t *tty; /* associated TTY structure */

	int icount; /* number of bytes in the input buffer */
	char *ihead; /* next free spot in input buffer */
	char *itail; /* first byte to give to TTY */
	bool_t idevready; /* nonzero if we are ready to receive (RTS) */
	char cts; /* normally 0, but MS_CTS if CLOCAL is set */

	unsigned char ostate; /* combination of flags: */
#define ODONE          1	/* output completed (< output enable bits) */
#define ORAW           2	/* raw mode for xoff disable (< enab. bits) */
#define OWAKEUP        4	/* tty_wakeup() pending (asm code only) */
#define ODEVREADY MS_CTS	/* external device hardware ready (CTS) */
#define OQUEUED     0x20	/* output buffer not empty */
#define OSWREADY    0x40	/* external device software ready (no xoff) */
#define ODEVHUP  MS_RLSD	/* external device has dropped carrier */
#define OSOFTBITS  (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
	/* user-defined bits */
#if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
	/* a weak sanity check */
#error				/* bits are not unique */
#endif
	unsigned char oxoff; /* char to stop output */
	bool_t inhibited; /* output inhibited? (follows tty_inhibited) */
	bool_t drain; /* if set drain output and reconfigure line */
	int ocount; /* number of bytes in the output buffer */
	char *ohead; /* next free spot in output buffer */
	char *otail; /* next char to output */

#if (MACHINE == IBM_PC)
	port_t xmit_port; /* i/o ports */
	port_t recv_port;
	port_t div_low_port;
	port_t div_hi_port;
	port_t int_enab_port;
	port_t int_id_port;
	port_t line_ctl_port;
	port_t modem_ctl_port;
	port_t line_status_port;
	port_t modem_status_port;
#endif

	unsigned char lstatus; /* last line status */
	unsigned char pad; /* ensure alignment for 16-bit ints */
	unsigned framing_errors; /* error counts (no reporting yet) */
	unsigned overrun_errors;
	unsigned parity_errors;
	unsigned break_interrupts;

	int irq; /* irq for this line */
	int irq_hook_id; /* interrupt hook */

	char ibuf[RS_IBUFSIZE]; /* input buffer */
	char obuf[RS_OBUFSIZE]; /* output buffer */
}rs232_t;

PRIVATE rs232_t rs_lines[NR_RS_LINES];

#if (MACHINE == IBM_PC)
/* 8250 base addresses. */
PRIVATE port_t addr_8250[] = {
	0x3F8, /* COM1 */
	0x2F8, /* COM2 */
	0x3E8, /* COM3 */
	0x2E8, /* COM4 */
};
#endif

FORWARD _PROTOTYPE( void in_int, (rs232_t *rs) );
FORWARD _PROTOTYPE( void line_int, (rs232_t *rs) );
FORWARD _PROTOTYPE( void modem_int, (rs232_t *rs) );
FORWARD _PROTOTYPE( int rs_write, (tty_t *tp, int try) );
FORWARD _PROTOTYPE( void rs_echo, (tty_t *tp, int c) );
FORWARD _PROTOTYPE( int rs_ioctl, (tty_t *tp, int try) );
FORWARD _PROTOTYPE( void rs_config, (rs232_t *rs) );
FORWARD _PROTOTYPE( int rs_read, (tty_t *tp, int try) );
FORWARD _PROTOTYPE( int rs_icancel, (tty_t *tp, int try) );
FORWARD _PROTOTYPE( int rs_ocancel, (tty_t *tp, int try) );
FORWARD _PROTOTYPE( void rs_ostart, (rs232_t *rs) );
FORWARD _PROTOTYPE( int rs_break, (tty_t *tp, int try) );
FORWARD _PROTOTYPE( int rs_close, (tty_t *tp, int try) );
FORWARD _PROTOTYPE( void out_int, (rs232_t *rs) );
FORWARD _PROTOTYPE( void rs232_handler, (rs232_t *rs) );

/* XXX */
PRIVATE void lock(void) {}
PRIVATE void unlock(void) {}

/*Added by Jeroen*/
char value[5];

PRIVATE int my_inb(port_t port)
{
	int r;
	unsigned long v = 0;
	r = sys_inb(port, &v);
	if (r != OK)
	printf("RS232 warning: failed inb 0x%x\n", port);

	return (int) v;
}

/*===========================================================================*
 *				rs_write				     *
 *===========================================================================*/
PRIVATE int rs_write(register tty_t *tp, int try)
{
	/* (*devwrite)() routine for RS232. */

	rs232_t *rs = tp->tty_priv;
	int count, ocount;

	if (rs->inhibited != tp->tty_inhibited) {
		/* Inhibition state has changed. */
		lock();
		rs->ostate |= OSWREADY;
		if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
		unlock();
		rs->inhibited = tp->tty_inhibited;
	}

	if (rs->drain) {
		/* Wait for the line to drain then reconfigure and continue output. */
		if (rs->ocount > 0) return 0;
		rs->drain = FALSE;
		rs_config(rs);
	}

	/* While there is something to do. */
	for (;;) {
		ocount = buflen(rs->obuf) - rs->ocount;
		count = bufend(rs->obuf) - rs->ohead;
		if (count > ocount) count = ocount;
		if (count > tp->tty_outleft) count = tp->tty_outleft;
		if (count == 0 || tp->tty_inhibited) {
			if (try) return 0;
			break;
		}
		if (try) return 1;

		/* Copy from user space to the RS232 output buffer. */
		if(tp->tty_out_safe) {
			sys_safecopyfrom(tp->tty_outproc, tp->tty_out_vir_g,
					tp->tty_out_vir_offset, (vir_bytes) rs->ohead, count, D);
		} else {
			sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir_g,
					SELF, D, (vir_bytes) rs->ohead, (phys_bytes) count);
		}

		/* Perform output processing on the output buffer. */
		out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
		if (count == 0) break;

		/* Assume echoing messed up by output. */
		tp->tty_reprint = TRUE;

		/* Bookkeeping. */
		lock(); /* protect interrupt sensitive rs->ocount */
		rs->ocount += ocount;
		rs_ostart(rs);
		unlock();
		if ((rs->ohead += ocount) >= bufend(rs->obuf))
		rs->ohead -= buflen(rs->obuf);
		if(tp->tty_out_safe) {
			tp->tty_out_vir_offset += count;
		} else {
			tp->tty_out_vir_g += count;
		}
		tp->tty_outcum += count;
		if ((tp->tty_outleft -= count) == 0) {
			/* Output is finished, reply to the writer. */
			if(tp->tty_outrepcode == TTY_REVIVE) {
				notify(tp->tty_outcaller);
				tp->tty_outrevived = 1;
			} else {
				tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
						tp->tty_outproc, tp->tty_outcum);
				tp->tty_outcum = 0;
			}
		}
	}
	if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
		/* Oops, the line has hung up. */
		if(tp->tty_outrepcode == TTY_REVIVE) {
			notify(tp->tty_outcaller);
			tp->tty_outrevived = 1;
		} else {
			tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
					tp->tty_outproc, EIO);
			tp->tty_outleft = tp->tty_outcum = 0;
		}
	}

	return 1;
}

/*===========================================================================*
 *				rs_echo					     *
 *===========================================================================*/
PRIVATE void rs_echo(tp, c)
tty_t *tp; /* which TTY */
int c; /* character to echo */
{
	/* Echo one character.  (Like rs_write, but only one character, optionally.) */

	rs232_t *rs = tp->tty_priv;
	int count, ocount;

	ocount = buflen(rs->obuf) - rs->ocount;
	if (ocount == 0) return; /* output buffer full */
	count = 1;
	*rs->ohead = c; /* add one character */

	out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
	if (count == 0) return;

	lock();
	rs->ocount += ocount;
	rs_ostart(rs);
	unlock();
	if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
}

/*===========================================================================*
 *				rs_ioctl				     *
 *===========================================================================*/
PRIVATE int rs_ioctl(tty_t *tp, int dummy)
/* tp;			which TTY */
{
	/* Reconfigure the line as soon as the output has drained. */
	rs232_t *rs = tp->tty_priv;

	rs->drain = TRUE;
	return 0; /* dummy */
}

/*===========================================================================*
 *				rs_config				     *
 *===========================================================================*/
PRIVATE void rs_config(rs232_t *rs)
/* rs			which line */
{
	/* Set various line control parameters for RS232 I/O.
	 * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
	 * The 8250 can't handle split speed, so we use the input speed.
	 */

	tty_t *tp = rs->tty;
	int divisor;
	int line_controls;
	static struct speed2divisor {
		speed_t speed;
		int divisor;
	}s2d[] = {
#if (MACHINE == IBM_PC)
		{	B50, UART_FREQ / 50},
#endif
		{	B75, UART_FREQ / 75},
		{	B110, UART_FREQ / 110},
		{	B134, UART_FREQ * 10 / 1345},
		{	B150, UART_FREQ / 150},
		{	B200, UART_FREQ / 200},
		{	B300, UART_FREQ / 300},
		{	B600, UART_FREQ / 600},
		{	B1200, UART_FREQ / 1200},
#if (MACHINE == IBM_PC)
		{	B1800, UART_FREQ / 1800},
#endif
		{	B2400, UART_FREQ / 2400},
		{	B4800, UART_FREQ / 4800},
		{	B9600, UART_FREQ / 9600},
		{	B19200, UART_FREQ / 19200},
#if (MACHINE == IBM_PC)
		{	B38400, UART_FREQ / 38400},
		{	B57600, UART_FREQ / 57600},
		{	B115200, UART_FREQ / 115200L},
#endif
	};
	struct speed2divisor *s2dp;

	/* RS232 needs to know the xoff character, and if CTS works. */
	rs->oxoff = tp->tty_termios.c_cc[VSTOP];
	rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;

	/* Look up the 8250 rate divisor from the output speed. */
	divisor = 0;
	for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
		if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
	}
	if (divisor == 0) return; /* B0? */

	/* Compute line control flag bits. */
	line_controls = 0;
	if (tp->tty_termios.c_cflag & PARENB) {
		line_controls |= LC_PARITY;
		if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
	}
	if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
	line_controls |= (tp->tty_termios.c_cflag & CSIZE) >> 2;

	/* Lock out interrupts while setting the speed. The receiver register is
	 * going to be hidden by the div_low register, but the input interrupt
	 * handler relies on reading it to clear the interrupt and avoid looping
	 * forever.
	 */
	lock();

	/* Select the baud rate divisor registers and change the rate. */
	sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
	sys_outb(rs->div_low_port, divisor);
	sys_outb(rs->div_hi_port, divisor >> 8);

	/* Change the line controls and reselect the usual registers. */
	sys_outb(rs->line_ctl_port, line_controls);

	rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
	if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
	rs->ostate &= ~ORAW;

	unlock();
}

/*===========================================================================*
 *				rs_init					     *
 *===========================================================================*/
PUBLIC void rs_init(tty_t *tp)
/* tp			which TTY */
{
	unsigned long dummy;
	/* Initialize RS232 for one line. */

	register rs232_t *rs;
	int line;
	port_t this_8250;
	int irq;
	char l[10];

	/* Associate RS232 and TTY structures. */
	line = tp - &tty_table[NR_CONS];

	/* See if kernel debugging is enabled; if so, don't initialize this
	 * serial line, making tty not look at the irq and returning ENXIO
	 * for all requests on it from userland. (The kernel will use it.)
	 */
	if(env_get_param(SERVARNAME, l, sizeof(l)-1) == OK && atoi(l) == line) {
		return;
	}

	rs = tp->tty_priv = &rs_lines[line];
	rs->tty = tp;

	/* Set up input queue. */
	rs->ihead = rs->itail = rs->ibuf;

	/* Precalculate port numbers for speed. Magic numbers in the code (once). */
	this_8250 = addr_8250[line];
	rs->xmit_port = this_8250 + 0;
	rs->recv_port = this_8250 + 0;
	rs->div_low_port = this_8250 + 0;
	rs->div_hi_port = this_8250 + 1;
	rs->int_enab_port = this_8250 + 1;
	rs->int_id_port = this_8250 + 2;
	rs->line_ctl_port = this_8250 + 3;
	rs->modem_ctl_port = this_8250 + 4;
	rs->line_status_port = this_8250 + 5;
	rs->modem_status_port = this_8250 + 6;

	/* Set up the hardware to a base state, in particular
	 *	o turn off DTR (MC_DTR) to try to stop the external device.
	 *	o be careful about the divisor latch.  Some BIOS's leave it enabled
	 *	  here and that caused trouble (no interrupts) in version 1.5 by
	 *	  hiding the interrupt enable port in the next step, and worse trouble
	 *	  (continual interrupts) in an old version by hiding the receiver
	 *	  port in the first interrupt.  Call rs_ioctl() early to avoid this.
	 *	o disable interrupts at the chip level, to force an edge transition
	 *	  on the 8259 line when interrupts are next enabled and active.
	 *	  RS232 interrupts are guaranteed to be disabled now by the 8259
	 *	  mask, but there used to be trouble if the mask was set without
	 *	  handling a previous interrupt.
	 */
	istop(rs); /* sets modem_ctl_port */
	rs_config(rs);
	sys_outb(rs->int_enab_port, 0);

	/* Clear any harmful leftover interrupts.  An output interrupt is harmless
	 * and will occur when interrupts are enabled anyway.  Set up the output
	 * queue using the status from clearing the modem status interrupt.
	 */
	sys_inb(rs->line_status_port, &dummy);
	sys_inb(rs->recv_port, &dummy);
	rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
	rs->ohead = rs->otail = rs->obuf;

	/* Enable interrupts for both interrupt controller and device. */
	irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;

	rs->irq = irq;
	rs->irq_hook_id = rs->irq; /* call back with irq line number */
	if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
		printf("RS232: Couldn't obtain hook for irq %d\n", irq);
	} else {
		if (sys_irqenable(&rs->irq_hook_id) != OK) {
			printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
		}
	}

	rs_irq_set |= (1 << irq);

	sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
			| IE_RECEIVER_READY | IE_TRANSMITTER_READY);

	/* Fill in TTY function hooks. */
	tp->tty_devread = rs_read;
	tp->tty_devwrite = rs_write;
	tp->tty_echo = rs_echo;
	tp->tty_icancel = rs_icancel;
	tp->tty_ocancel = rs_ocancel;
	tp->tty_ioctl = rs_ioctl;
	tp->tty_break = rs_break;
	tp->tty_close = rs_close;

	/* Tell external device we are ready. */
	istart(rs);

}

/*===========================================================================*
 *				rs_interrupt				     *
 *===========================================================================*/
PUBLIC void rs_interrupt(message *m)
{
	unsigned long irq_set;
	int i;
	rs232_t *rs;

	irq_set= m->NOTIFY_ARG;
	for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
	{
		if (irq_set & (1 << rs->irq))
		rs232_handler(rs);
	}
}

/*===========================================================================*
 *				rs_icancel				     *
 *===========================================================================*/
PRIVATE int rs_icancel(tty_t *tp, int dummy)
{
	/* Cancel waiting input. */
	rs232_t *rs = tp->tty_priv;

	lock();
	rs->icount = 0;
	rs->itail = rs->ihead;
	istart(rs);
	unlock();

	return 0; /* dummy */
}

/*===========================================================================*
 *				rs_ocancel				     *
 *===========================================================================*/
PRIVATE int rs_ocancel(tty_t *tp, int dummy)
{
	/* Cancel pending output. */
	rs232_t *rs = tp->tty_priv;

	lock();
	rs->ostate &= ~(ODONE | OQUEUED);
	rs->ocount = 0;
	rs->otail = rs->ohead;
	unlock();

	return 0; /* dummy */
}

/*===========================================================================*
 *				rs_read					     *
 *===========================================================================*/
PRIVATE int rs_read(tty_t *tp, int try)
{
	/* Process characters from the circular input buffer. */

	rs232_t *rs = tp->tty_priv;
	int icount, count, ostate;

	if (!(tp->tty_termios.c_cflag & CLOCAL)) {
		if (try) return 1;
		/* Send a SIGHUP if hangup detected. */
		lock();
		ostate = rs->ostate;
		rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
		unlock();
		if (ostate & ODEVHUP) {
			sigchar(tp, SIGHUP, 1);
			tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
			return 0;
		}
	}

	if (try) {
		if (rs->icount > 0)
		return 1;
		return 0;
	}

	while ((count = rs->icount) > 0) {
		icount = bufend(rs->ibuf) - rs->itail;
		if (count > icount) count = icount;

		/* Perform input processing on (part of) the input buffer. */
		if ((count = in_process(tp, rs->itail, count, -1)) == 0) break;

		lock(); /* protect interrupt sensitive variables */
		rs->icount -= count;
		if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
		unlock();
		if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
	}

	return 0;
}

/*===========================================================================*
 *				rs_ostart				     *
 *===========================================================================*/
PRIVATE void rs_ostart(rs232_t *rs)
{
	/* Tell RS232 there is something waiting in the output buffer. */

	rs->ostate |= OQUEUED;
	if (txready(rs)) out_int(rs);
}

/*===========================================================================*
 *				rs_break				     *
 *===========================================================================*/
PRIVATE int rs_break(tty_t *tp, int dummy)
{
	/* Generate a break condition by setting the BREAK bit for 0.4 sec. */
	rs232_t *rs = tp->tty_priv;
	unsigned long line_controls;

	sys_inb(rs->line_ctl_port, &line_controls);
	sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
	/* XXX */
	/* milli_delay(400); *//* ouch */
	printf("RS232 break\n");
	sys_outb(rs->line_ctl_port, line_controls);
	return 0; /* dummy */
}

/*===========================================================================*
 *				rs_close				     *
 *===========================================================================*/
PRIVATE int rs_close(tty_t *tp, int dummy)
{
	/* The line is closed; optionally hang up. */
	rs232_t *rs = tp->tty_priv;

	if (tp->tty_termios.c_cflag & HUPCL) {
		sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
	}
	return 0; /* dummy */
}

/* Low level (interrupt) routines. */

/*===========================================================================*
 *				rs232_handler				     *
 *===========================================================================*/
PRIVATE void rs232_handler(struct rs232 *rs)
{
	/* Interrupt hander for RS232. */

	while (TRUE) {
		unsigned long v;
		/* Loop to pick up ALL pending interrupts for device.
		 * This usually just wastes time unless the hardware has a buffer
		 * (and then we have to worry about being stuck in the loop too long).
		 * Unfortunately, some serial cards lock up without this.
		 */
		sys_inb(rs->int_id_port, &v);
		switch (v) {
			case IS_RECEIVER_READY:
			in_int(rs);
			continue;
			case IS_TRANSMITTER_READY:
			out_int(rs);
			continue;
			case IS_MODEM_STATUS_CHANGE:
			modem_int(rs);
			continue;
			case IS_LINE_STATUS_CHANGE:
			line_int(rs);
			continue;
		}
		return;
	}
}

/*===========================================================================*
 *				in_int					     *
 *===========================================================================*/
PRIVATE void in_int(register rs232_t *rs)
/* rs	line with input interrupt */
{
	/* Read the data which just arrived.
	 * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
	 * it and restart output (any char does this, not just xon).
	 * Put data in the buffer if room, otherwise discard it.
	 * Set a flag for the clock interrupt handler to eventually notify TTY.
	 */

	unsigned long c;
	int i;
	int j;
	int counter;
	char temp[2];
	int valueInt;

#if 0	/* Enable this if you want serial input in the kernel */
	return;
#endif

	sys_inb(rs->recv_port, &c);

	if (!(rs->ostate & ORAW)) {
		if (c == rs->oxoff) {
			rs->ostate &= ~OSWREADY;
		} else
		if (!(rs->ostate & OSWREADY)) {
			rs->ostate |= OSWREADY;
			if (txready(rs)) out_int(rs);
		}
	}

	if (rs->icount == buflen(rs->ibuf))
	{
		printf("in_int: discarding byte\n");
		return; /* input buffer full, discard */
	}

	if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
	*rs->ihead = c;
	if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
	if (rs->icount == 1) {
		rs->tty->tty_events = 1;
		force_timeout();
	}
	else

	/*Added by projectgroep*/
	if(rs->ihead!="")
	{
		i = rs->ihead;
		j = &rs->ibuf;
		i = i-j;
		i = i-1;
		if(rs->ibuf[i]==';')/*send value when ';' is next char*/
		{
			int s;
			message m;
			/*convert string to int*/
			valueInt = atoi(value);

			/*send message with value to control loop driver*/
				m.m_type =RS232_TO_CTRL;
				m.RS232_CTRL =valueInt;
				if (OK != (s = send(PM_PROC_NR, &m))) {
					panic("Did not send endpoint from rs232 to ctrl\n");
				}

			strcpy(value,"");
		}
		else
		{
			/*combine characters in a string till a ';'*/
			temp[0]=rs->ibuf[i];
			temp[1]='\0';
			strcat(value,temp);
		}
	}
}

/*===========================================================================*
 *				line_int				     *
 *===========================================================================*/
PRIVATE void line_int(register rs232_t *rs)
/* rs		line with line status interrupt */
{
	/* Check for and record errors. */

	unsigned long s;
	sys_inb(rs->line_status_port, &s);
	rs->lstatus = s;
	if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
	if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
	if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
	if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
}

/*===========================================================================*
 *				modem_int				     *
 *===========================================================================*/
PRIVATE void modem_int(register rs232_t *rs)
/* rs	line with modem interrupt */
{
	/* Get possibly new device-ready status, and clear ODEVREADY if necessary.
	 * If the device just became ready, restart output.
	 */

	if (devhup(rs)) {
		rs->ostate |= ODEVHUP;
		rs->tty->tty_events = 1;
		force_timeout();
	}

	if (!devready(rs))
	rs->ostate &= ~ODEVREADY;
	else if (!(rs->ostate & ODEVREADY)) {
		rs->ostate |= ODEVREADY;
		if (txready(rs)) out_int(rs);
	}
}

/*===========================================================================*
 *				out_int					    *
 *===========================================================================*/
PRIVATE void out_int(register rs232_t *rs)
/* rs;	line with output interrupt */
{
	/* If there is output to do and everything is ready, do it (local device is
	 * known ready).
	 * Notify TTY when the buffer goes empty.
	 */

	if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
		/* Bit test allows ORAW and requires the others. */
		sys_outb(rs->xmit_port, *rs->otail);
		if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
		if (--rs->ocount == 0) {
			rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */
			rs->tty->tty_events = 1;
			force_timeout();
		} else
		if (rs->ocount == RS_OLOWWATER) { /* running low? */
			rs->tty->tty_events = 1;
			force_timeout();
		}
	}
}
#endif /* NR_RS_LINES > 0 */

